
import java.awt.Point;
import java.util.ArrayList;

import pulpcore.image.filter.Blur;
import pulpcore.image.filter.Glow;
import pulpcore.image.filter.Grayscale;
import pulpcore.scene.Scene2D;
import pulpcore.sprite.ImageSprite;
import quicktime.std.anim.Sprite;


public class Character {
	private ImageSprite body;
	private String name;
	private int level;
	private int health;
	private int atk;
	private int def;
	private int move;
	private int speed;
	private int crit;
	private int Mresist;
	private int dodge;
	private int exp;
	private boolean selected;
	private Point position;
	private String team;
	private boolean acted;
	private boolean isDead;
	private Role job;
	
	public Character(String n, ImageSprite s, Role r, Point pos, int lvl, int hp, int atk, int def, int mve, int spd, int crt, int res, int dodge){
		name = n;
		body = s;
		job = r;
		level = lvl;
		health = hp;
		this.atk = atk;
		this.def = def;
		move = mve;
		speed = spd;
		crit = crt;
		Mresist = res;
		this.dodge = dodge;
		selected = false;
		position = pos;
		isDead = false;
		acted = false;
	}

	public ImageSprite getSprite(){
		return body;
	}
	public int getPositionX(){
		return position.x;
	}
	public int getPositionY(){
		return position.y;
	}
	
	public Point getPosition(){
		return position;
	}
	
	public void setTeam(String t){
		team = t;
	}
	
	public String getTeam(){
		return team;
	}
	
	public String getName(){
		return name;
	}
	
	public int getAtk(){
		return atk;
	}
	
	public int getDef(){
		return def;
	}
	
	public int getRes(){
		return Mresist;
	}
	
	public int getCrit(){
		return crit;
	}
	
	public int getRange(){
		return job.getRange();
	}
	
	public String getWeapon(){
		return job.getWeapon();
	}
	
	public int getSpeed(){
		return speed;
	}
	
	public int getHealth(){
		return health;
	}
	
	public int getDodge(){
		return dodge;
	}
	
	
	public int getLevel(){
		return level;
	}
	public int getMove(){
		return move;
	}
	
	public boolean getActed(){
		return acted;
	}
	
	public void justActed(boolean act){
		acted = act;
		if(act){
			unSelected();
		}
	}
	
	
	
	public void gainExp(int elvl, boolean kill){
		int diff = elvl - level;
		if(diff<0)
			diff = 0;
		if(kill){
			exp += 10+10*diff;
		}else{
			exp += 2+2*diff;
		}
		if(exp>100){
			levelUp();
			exp -= 100;
		}
	} 
	
	public void levelUp(){
		System.out.println("Level up!");
	}
	
	public int reduceHealth(int damage){
		health -= damage;
		if(health<0){
			health = 0;
		}
		return health;
	}
	
	//
	public boolean isHit(int accuracy){
		if((int)Math.random()*100<accuracy)
			return true;
		else
			return false;
	}
	
	//
	public boolean isCrit(double critical){
		if((int)(Math.random()*100)<critical)
			return true;
		else
			return false;
	}
	
	public void die(){
		//insert death animation and remove from grid
		System.out.println(name + " is dead!");
		isDead = true;
		body.removeFromParent();
	}
	
	public boolean isDead(){
		return isDead;
	}
	
	public boolean canAttack(Character x){
		if(!team.equals(x.getTeam()))
			return true;
		else 
			return false;
	}
	
	
	//determines if the character's weapon is super effective against the enemy's weapon
	public boolean isSuper(Character x){
		String weap2 = x.getWeapon();
		String weap = getWeapon();
		if(weap.equals("axe") && weap2.equals("lance"))
			return true;
		if(weap.equals("lance") && weap2.equals("sword"))
			return true;
		if(weap.equals("sword") && weap2.equals("axe"))
			return true;
		else
			return false;
	}
	
	public void battle(Character x){
		//attacker attacks first
		System.out.println("Battle!");
		System.out.println("  Attack!");
		attack(x);
		//if enemy's health is greater than 0, it counter attacks
		if(x.getHealth()>0 && x.getRange()>=(Math.abs(x.getPositionX() - this.getPositionX())+Math.abs(x.getPositionY() - this.getPositionY()))){
			System.out.println("  Counterattack!");
			x.attack(this);
		//else enemy dies and this character gains exp
		}
		else{
			x.die();
			System.out.println("Died after first hit");
			gainExp(x.getLevel(), true);
		}
		//if both characters are alive and this character's speed is twice the enemy's speed, attack again
		if(x.getHealth()>0 && getHealth()>0 && getSpeed() > x.getSpeed()*2){
			System.out.println("  Second Attack!");
			attack(x);
			//if enemy's health is 0, the enemy dies and the character gains exp
			if(x.getHealth()<=0){
				x.die();
				System.out.println("Died after second hit");
				gainExp(x.getLevel(), true);
			}
		}
		//if the this character's health is 0 or less, this character dies and the enemy gains exp
		else if(getHealth()<=0){
			this.die();
			x.gainExp(this.getLevel(), true);
		}
		//if neither character dies, both gain exp
		else{
			gainExp(x.getLevel(), false);
			x.gainExp(this.getLevel(), false);
		}
		System.out.println(name + ": " + getHealth());
		System.out.println(x.getName() + ": " + x.getHealth());
	}
	//returns the hit chance, damage, and critical chance of a battle
	public int[] attackPlan(Character x){
		int[] outcome = new int[3];
		double damage = 0;
		int hit = 100 - x.getDodge();
		int critical = crit;
		//if the weapon is magic, the damage is calculated using the defender's resistance rather than defense
		if(getWeapon().equals("magic"))
			damage = (double)(getAtk() - x.getRes());
		else
			damage = (double)(getAtk() - x.getDef());
		if(damage<0)
			damage = 0;
		if(hit<0){
			hit=0;
		}
		if(isSuper(x)){
			//improves damage, hit, and crit if the attacker's weapon is super effective
			System.out.println(getName() + " is super effective!");
			damage = damage*1.5;
			hit += 20;
			critical += 5;
		}
		else if(x.isSuper(this)){
			//reduces damage, hit, and crit if the defender's weapon is super effective
			System.out.println(x.getName() + " is super effective!");
			damage = damage*.8;
			hit -= 20;
			critical -= 5;
		}
		outcome[0] = hit;
		outcome[1] = (int)damage;
		outcome[2] = critical;
		System.out.println("     Hit Chance: " + outcome[0]);
		System.out.println("     Damage: " + outcome[1]);
		System.out.println("     Critical Chance: " + outcome[2]);
		return outcome;
	}
	
	//carries out the battle
	public void attack(Character x){
		int[] stats = attackPlan(x);
		int hit = stats[0];
		int damage = stats[1];
		int critical = stats [2];
		if(isHit(hit)){
			if(isCrit(critical)){
				damage = damage*2;
				//insert crit animation
				System.out.println("Critical!");
				x.reduceHealth(damage);
			}
			else{
				//insert attack animation
				System.out.println("Hit!");
				x.reduceHealth(damage);
			}
		}
		else{
			//insert dodge animation
			System.out.println("Missed!");
		}
	}
	
	public void unSelected(){
		selected = false;
	}
	
	public Character selected(Tile[][] tilegrid){
		if (tilegrid[position.x][position.y].getSprite().isMousePressed() && !acted){
			selected=true;
			System.out.println(this);
			return this;
		}
		else
		{
			return null;
		}
	}
	
	public boolean isClicked(){
		if (body.isMousePressed()){
			return true;
		}
		else
			return false;
	}
	
	
	public void setPosition(int x, int y)
	{
		position.x = x;
		position.y = y;
	}
	
	
	

	public String toString(){
		return name + " Health: " + health + "Range: "+getRange()+ "Move: "+getMove();
		
	}
	public String toString2(){
		return "Weapon: "+getWeapon()+ "  Atk: "+atk + " Def:" + def ;
		
	}
	
	
	
	//AI STARTS HERE
	
	public ArrayList<Character> getTargets(PlayerTeam x, EnemyTeam y, String[] map){
		ArrayList<Character> targets = new ArrayList<Character>();
		for(Character c : x.getTeam()){
			if(inRange(c.getPosition(), move+getRange()) && getAdjacentLocations(c.getPosition(), x, y, map).size() != 0){
				targets.add(c);
			}
		}
		return targets;
	}
	
	public Character chooseTarget(ArrayList<Character> targets, PlayerTeam x, EnemyTeam y, String[] map){
		Character target = targets.get(0);
		for(int i = 1; i<targets.size(); i++){
			Character c = targets.get(i);
			int[] chosen = attackPlan(target);
			int[] alternate = attackPlan(c);
			if(alternate[1]>0 && c.getHealth() - alternate[1]< target.getHealth() - chosen[1]){
				target = c;
			}
		}
		return target;
	}
	
	public void setPosition(Point p){
		position = p;
		body.moveTo(p.x*100,p.y*80-80, 300);
		System.out.println("AI");
	}
	
	public ArrayList<Point> getAdjacentLocations(Point p, PlayerTeam x, EnemyTeam y, String [] map){
		ArrayList<Point> adjacent = new ArrayList<Point>();
		Point a = new Point(p.x, p.y+1);
		Point b = new Point(p.x+1, p.y);
		Point c = new Point(p.x, p.y-1);
		Point d = new Point(p.x+1, p.y);
		adjacent.add(a);
		adjacent.add(b);
		adjacent.add(c);
		adjacent.add(d);
		for(int i = 0; i<adjacent.size(); i++){
			Point z = adjacent.get(i);
			if(map[z.y].charAt(z.x)=='W' || (x.getOccupiedLocations(y).contains(z) && !z.equals(position)) || p.x<0 || p.y<0 || !inRange(z, move)){
				adjacent.remove(z);
				i--;
			}
		}
		return adjacent;
	}
	
	public void moveTowardsPoint(Point p, PlayerTeam x, EnemyTeam y){
		
		
	}
	
	public boolean inRange(Point c, int range){
		int xdiff = Math.abs(position.x - c.x);
		int ydiff = Math.abs(position.y - c.y);
		if(xdiff + ydiff <= range){
			return true;
		}
		else{
			return false;
		}
	}
}